package com.lenovo.leos.filebrowser.engine.operation;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;

import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnDismissListener;
import android.net.Uri;
import android.os.Message;
import android.widget.Toast;

import com.lenovo.leos.filebrowser.ui.BaseActivity;
import com.lenovo.leos.filebrowser.R;
import com.lenovo.leos.filebrowser.engine.util.FileClipBoard;
import com.lenovo.leos.filebrowser.engine.util.FileHandler;
import com.lenovo.leos.filebrowser.engine.util.FileThread;
import com.lenovo.leos.filebrowser.engine.util.FileUtil;
import com.lenovo.leos.filebrowser.engine.util.StorageManager;
import com.lenovo.leos.filebrowser.engine.util.FileHandler.MsgHandleFunc;
import com.lenovo.leos.filebrowser.engine.util.Log;

public class FilePaste {
	private static final String TAG = "FilePaste"; 
	
	private static final int COPY_BUFFER_MAXSIZE = 0x40000;
	
	public static final int MSG_RES_PASTE_OK = R.string.msg_paste_res_ok;
	public static final int MSG_RES_PASTE_FAILED = R.string.msg_paste_res_failed;
	public static final int MSG_RES_PASTE_CANCEL = R.string.msg_paste_res_cancel;
	
	private static final int MSG_SHOW_PROGRESS_DIALOG = R.string.msg_paste_show_progress_dialog;
//	private static final int MSG_UPDATE_PROGRESS = R.string.msg_delete_update_progress;
	private static final int MSG_DISMISS_ALL_DIALOG = R.string.msg_paste_dismiss_all_dialog;
	
	private static final int MSG_SRC_INVALID = R.string.msg_paste_src_invalid;

	private static final int INVALID_VALUE = -1;
	private static final int USER_FEEDBACK_SKIP = 0;
	private static final int USER_FEEDBACK_SKIP_ALL = 1;
	private static final int USER_FEEDBACK_CANCEL = 2;
	private static final int USER_FEEDBACK_REPLACE_FILE = 3;
	private static final int USER_FEEDBACK_REPLACE_DIR = 4;
	
	private static final int MSG_PASTE_NOSPACE = 7;
	private static final int MSG_PASTE_SAME_FILE = 8;
	private static final int MSG_PASTE_SAME_FILE_ONLY_ONE = 9;
	private static final int MSG_PASTE_SAME_DIR = 10;
	private static final int MSG_PASTE_SAME_DIR_ONLY_ONE = 11;
	private static final int MSG_PASTE_SRC_INVALID = 13;
		
	private static MsgHandleFunc onSameFileHandler = new FileHandler.MsgHandleFunc() {
		// @Override
		public boolean onMessage(Message msg) {
			if (null != sActivity){
				new AlertDialog.Builder(sActivity)
	            //.setIcon(R.drawable.alert_dialog_icon)
	            .setTitle(R.string.remind)
	            .setMessage(msg.obj.toString() + "\n" + 
	            		sActivity.getResources().getString(R.string.samefileexist))
	            .setPositiveButton(R.string.alert_dialog_on_samefile_yes, 
	            		new DialogInterface.OnClickListener() {
	                public void onClick(DialogInterface dialog, int whichButton) {
	                	siFeedBack = USER_FEEDBACK_REPLACE_FILE;
	                	if(null != sPasteThread)
	                		sPasteThread.resumeIt();
	                }})
	            .setNeutralButton(R.string.alert_dialog_on_samefile_no, 
	            		new DialogInterface.OnClickListener(){
	        	    public void onClick(DialogInterface dialog, int whichButton) {
	        	    	siFeedBack = USER_FEEDBACK_SKIP;
	        	    	if(null != sPasteThread)
	        	    		sPasteThread.resumeIt();
	                }})
	            .setNegativeButton(R.string.alert_dialog_on_samefile_abort, new DialogInterface.OnClickListener() {
	                public void onClick(DialogInterface dialog, int whichButton) {
	                	siFeedBack = USER_FEEDBACK_CANCEL;
	                	sPasteThread.stopIt();
	                }
			   }).show().setOnDismissListener(new OnDismissListener(){
	               public void onDismiss(DialogInterface dialog) {
	                  // TODO Auto-generated method stub
	            	   if(siFeedBack != USER_FEEDBACK_REPLACE_FILE &&
	            			   siFeedBack != USER_FEEDBACK_SKIP && 
	            			   siFeedBack != USER_FEEDBACK_CANCEL){
	            		   sPasteThread.stopIt();
	            		   dismissAllDlg();
	            	   }
	            	   siFeedBack = INVALID_VALUE;
	               }
	             }); 				
			}else{
				Log.e(TAG, "sActivity is null");
			}
			return true;
		}
	};
	
	private static MsgHandleFunc onSameFileOnlyOneHandler = new FileHandler.MsgHandleFunc() {
		// @Override
		public boolean onMessage(Message msg) {
			if (null != sActivity){
				siFeedBack = INVALID_VALUE;
				
				new AlertDialog.Builder(sActivity)
	            //.setIcon(R.drawable.alert_dialog_icon)
	            .setTitle(R.string.remind)
	            .setMessage(msg.obj.toString() + "\n" + 
	            		sActivity.getResources().getString(R.string.samefileexist))
	            .setPositiveButton(R.string.alert_dialog_on_samefile_yes, 
	            		new DialogInterface.OnClickListener() {
	                public void onClick(DialogInterface dialog, int whichButton) {
	                	siFeedBack = USER_FEEDBACK_REPLACE_FILE;
	                	if(null != sPasteThread)
	                		sPasteThread.resumeIt();
	                }})
	            .setNegativeButton(R.string.alert_dialog_on_samefile_abort, new DialogInterface.OnClickListener() {
	                public void onClick(DialogInterface dialog, int whichButton) {
	                	siFeedBack = USER_FEEDBACK_CANCEL;
	                	if(null != sPasteThread)
	                		sPasteThread.resumeIt();
	                }
			   })
			   .show()
			   .setOnDismissListener(new OnDismissListener(){
	               public void onDismiss(DialogInterface dialog) {
	                  // TODO Auto-generated method stub
	            	   if(siFeedBack != USER_FEEDBACK_REPLACE_FILE &&
	            			   siFeedBack != USER_FEEDBACK_CANCEL){
	            		   sPasteThread.stopIt();
	            		   dismissAllDlg();
	            	   }
	            	   siFeedBack = INVALID_VALUE;
	               }
	             }); 				
			}else{
				Log.e(TAG, "sActivity is null");
			}
			return true;
		}
	};
	
	
	private static MsgHandleFunc onSameDirHandler = new FileHandler.MsgHandleFunc() {
		// @Override
		public boolean onMessage(Message msg) {
			// TODO Auto-generated method stub
			if (null != sActivity) {
				siFeedBack = INVALID_VALUE;

				new AlertDialog.Builder(sActivity)
				.setTitle(R.string.remind)
				.setMessage(msg.obj.toString() + "\n"
						+ sActivity.getResources().getString(R.string.dir_exist))
				.setPositiveButton(R.string.alert_dialog_on_samefile_yes,
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int whichButton) {
								siFeedBack = USER_FEEDBACK_REPLACE_DIR;
								if (null != sPasteThread)
									sPasteThread.resumeIt();
							}
				})
				.setNeutralButton(R.string.alert_dialog_on_samefile_no,
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int whichButton) {
								siFeedBack = USER_FEEDBACK_SKIP;
								if (null != sPasteThread)
									sPasteThread.resumeIt();
								}
				})
				.setNegativeButton(R.string.alert_dialog_on_samefile_abort,
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int whichButton) {
								siFeedBack = USER_FEEDBACK_CANCEL;
								if (null != sPasteThread)
									sPasteThread.resumeIt();
							}
				})
				.show()
				.setOnDismissListener(new OnDismissListener() {
						public void onDismiss(DialogInterface dialog) {
							if (siFeedBack != USER_FEEDBACK_REPLACE_FILE
								&& siFeedBack != USER_FEEDBACK_SKIP
								&& siFeedBack != USER_FEEDBACK_CANCEL) {
								if (null != sPasteThread)
									sPasteThread.stopIt();
								dismissAllDlg();
							}
							siFeedBack = INVALID_VALUE;
						}
				});
			} else {
				Log.e(TAG, "sActivity is null");
			}
			return false;
		}
	};
	
	private static MsgHandleFunc onSameDirOnlyOneHandler = new FileHandler.MsgHandleFunc() {
//		@Override
		public boolean onMessage(Message msg) {
			// TODO Auto-generated method stub
			if (null != sActivity) {
				siFeedBack = INVALID_VALUE;
				
				new AlertDialog.Builder(sActivity)
				.setTitle(R.string.remind)
				.setMessage(msg.obj.toString() + "\n" + 
						sActivity.getResources().getString(R.string.dir_exist))
				.setPositiveButton(R.string.alert_dialog_on_samefile_yes,
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int whichButton) {
								siFeedBack = USER_FEEDBACK_REPLACE_DIR;
								if (null != sPasteThread)
									sPasteThread.resumeIt();
						}
				})
				.setNegativeButton(R.string.alert_dialog_on_samefile_abort, 
						new DialogInterface.OnClickListener(){
							public void onClick(DialogInterface dialog,int whichButton) {
								siFeedBack = USER_FEEDBACK_CANCEL;
								if (null != sPasteThread)
									sPasteThread.resumeIt();
						}
				})
				.show()
				.setOnDismissListener(new OnDismissListener(){
						public void onDismiss(DialogInterface dialog) {
							if(siFeedBack != USER_FEEDBACK_REPLACE_FILE &&
			            			   siFeedBack != USER_FEEDBACK_CANCEL){
			            		   sPasteThread.stopIt();
			            		   dismissAllDlg();
			            	   }
			            	   siFeedBack = INVALID_VALUE;
						}
				});
			}
			return false;
		}
	};
	

	private static MsgHandleFunc onNoSpaceHandler = new FileHandler.MsgHandleFunc() {
//		@Override
		public boolean onMessage(Message msg) {
			// TODO Auto-generated method stub
			if (null != sActivity){
				new AlertDialog.Builder(sActivity)
				.setTitle(R.string.remind)
				.setMessage(R.string.nospaceonsd)
				.setPositiveButton(R.string.alert_dialog_ok,
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int whichButton) {
								if (null != sPasteThread)
									sPasteThread.resumeIt();
							}
				})
				.show()
				.setOnDismissListener(new OnDismissListener(){
						public void onDismiss(DialogInterface dialog) {
							if (null != sPasteThread)
								sPasteThread.resumeIt();
						}
				});
			}else{
				Log.e(TAG, "sActivity is null");
			}
			
			return false;
		}
	};
	
	// [improve]
	private static MsgHandleFunc onSrcInvalidHandler = new FileHandler.MsgHandleFunc() {
//		@Override
		public boolean onMessage(Message msg) {
			// TODO Auto-generated method stub
			if (null != sActivity) {
				siFeedBack = INVALID_VALUE;
			
				new AlertDialog.Builder(sActivity)
				.setTitle(R.string.remind)
				.setMessage(msg.obj.toString() + "\n" + 
						sActivity.getResources().getString(R.string.src_nonexistent))
				.setPositiveButton(R.string.src_nonexistent_skip,
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog,int whichButton) {
			        	    	siFeedBack = USER_FEEDBACK_SKIP;
			        	    	if(null != sPasteThread)
			        	    		sPasteThread.resumeIt();
							}
				})
				.setNeutralButton(R.string.src_nonexistent_skip_all, 
						new DialogInterface.OnClickListener(){
            	    		public void onClick(DialogInterface dialog, int whichButton) {
            	    			siFeedBack = USER_FEEDBACK_SKIP_ALL;
            	    			if(null != sPasteThread)
            	    				sPasteThread.resumeIt();
    	    		}
				})
				.setNegativeButton(R.string.src_nonexistent_cancel, 
						new DialogInterface.OnClickListener(){
							public void onClick(DialogInterface dialog,int whichButton) {
								siFeedBack = USER_FEEDBACK_CANCEL;
								if (null != sPasteThread)
									sPasteThread.resumeIt();
							}
				})
				.show()
				.setOnDismissListener(new OnDismissListener(){
		               public void onDismiss(DialogInterface dialog) {
							if (siFeedBack != USER_FEEDBACK_SKIP_ALL
									&& siFeedBack != USER_FEEDBACK_SKIP
									&& siFeedBack != USER_FEEDBACK_CANCEL) {
									if (null != sPasteThread)
										sPasteThread.stopIt();
									dismissAllDlg();
							}
							siFeedBack = INVALID_VALUE;
		               }
	            });	
			}else{
				Log.e(TAG, "sActivity is null");
			}
			
			return false;
		}
	};

	public static class FilePasteThread extends FileThread{

		List<File> mSrcFileList;
		
		boolean mbOnlyOneSrc = false;
		boolean isSkipAll = false;
		
		public FilePasteThread(BaseActivity ba) {
			super(ba, ba.getHandler());
			// TODO Auto-generated constructor stub
		}
				
		private void onShowProgDlg() {
			sendMsg(MSG_SHOW_PROGRESS_DIALOG);
		}
		
		private void onUpdateProgress(){
			if (null != sProgDlg)
				sProgDlg.incrementProgressBy(1);
		}
		
		public void onNoEnoughSpace(){
			sendMsgThenPause(MSG_PASTE_NOSPACE, null);
			stopIt();
		}
		
		public boolean onSameFileExist(String sFileName){
			Log.i(TAG, "==> onSameFileExist");
			
			if (!mbOnlyOneSrc){
				sendMsgThenPause(MSG_PASTE_SAME_FILE, sFileName);
			}else {
				sendMsgThenPause(MSG_PASTE_SAME_FILE_ONLY_ONE, sFileName);
				mbOnlyOneSrc = false;
			}
			
			switch(siFeedBack){
			case USER_FEEDBACK_REPLACE_FILE:
				return true;
			case USER_FEEDBACK_SKIP:
				return false;
			case USER_FEEDBACK_CANCEL:
				stopIt();
				return false;
			default:
				return false;
			}
		}
		
		// [improve] 5 buttons dialog
		public boolean onSameDirExist(String sDirName){	
			Log.i(TAG, "==> onSameDirExist");
			if (!mbOnlyOneSrc){
				sendMsgThenPause(MSG_PASTE_SAME_DIR, sDirName);
			}else {
				sendMsgThenPause(MSG_PASTE_SAME_DIR_ONLY_ONE, sDirName);
				mbOnlyOneSrc = false;
			}
			
			switch(siFeedBack){
			case USER_FEEDBACK_REPLACE_FILE:
				return true;
			case USER_FEEDBACK_SKIP:
				return false;
			case USER_FEEDBACK_CANCEL:
				stopIt();
				return false;
			default:
				return false;
			}
		}
		
		
		// true ==> skip false ==> cancel
		public boolean onSrcInvalid(File file) {
			// clear feedback	
			isSkipAll = false;
			sendMsgThenPause(MSG_SRC_INVALID, file);
			
			switch(siFeedBack){
			case USER_FEEDBACK_SKIP_ALL:
				isSkipAll = true;
				return true;
			case USER_FEEDBACK_SKIP:
				return true;
			case USER_FEEDBACK_CANCEL:
				stopIt();
				return false;
			default:
				return false;
			}
		}
		
		private void getFileCount(File file) {
			if (isStop() || null == file || !file.exists()) {
				return;
			}

			if (file.isDirectory()) {
				File[] files = file.listFiles();
				if (isStop() || 0 == files.length)
					return;

				if (files.length > 0) {

					int i;
					for (i = 0; i < files.length; i++) {
						getFileCount(files[i]);
					}
				}
			} else {
				siFileCount++;
			}
		}

		private void getFileCount(List<File> fileList) {
			if (isStop() || 0 == fileList.size()) {
				return;
			}

			for (File file : fileList) {
				if (isStop() || !file.exists()) {
					break;
				}
				getFileCount(file);
			}
		}
		
		private boolean hasEnoughSpaceForFile(File file) {
			Log.i(TAG, "==> hasEnoughSpaceForFile");
			
			if (null == file || !file.exists() || !file.isFile()){
				Log.e(TAG, "invalid file in hasEnoughSpaceForFile");
				return false;
			}else{
				StorageManager sm = StorageManager.getInstance(sActivity);
				if (null == sm){
					Log.e(TAG, "can not get storage manager");
					return false;
				}else{
					long free = sm.getStorageFreeSizeByFile(file);
					if (free > (long) (file.length() + 1024))
						return true;
					else
						return false;
				}
			}
		}
		
		//[improve] 
		private File getValidNewFolderName(File parent, String name){
			Log.i(TAG, "==> getValidNewFolderName");
			
			if (null == parent || !parent.isDirectory()
					|| null == name || 0 == name.length()){
				Log.e(TAG, "invalid parameter in getValidNewFolderName");
				return null;
			}
			
			String newName = null;
			File tempDes = null;
			newName = parent.getAbsolutePath() + "(" + 
				sActivity.getResources().getString(R.string.copy)  + ")";

			tempDes = new File(newName);
			int nIndex = 1; 
			while(tempDes.exists()){
				newName = parent.getAbsolutePath() + "(" + 
					sActivity.getResources().getString(R.string.copy) + 
					String.valueOf(nIndex) + ")";
				tempDes = new File(newName);
				nIndex += 1;
			}
			return tempDes;
		}
		
		private File getValidNewFileName(File parent, String name){
			Log.i(TAG, "==> getValidNewFileName");
			
			if (null == parent || !parent.isDirectory()
					|| null == name || 0 == name.length()){
				Log.e(TAG, "invalid parameter in getValidNewFolderName");
				return null;
			}
			
			String nameWithoutExt = FileUtil.getFileNameWithoutExt(name);
			String ext = FileUtil.getExtension(name);
			String newName = null;
			
			if(null == ext || 0 == ext.length()){
				newName = parent.getAbsolutePath() + "/" + nameWithoutExt +"(" + 
					sActivity.getResources().getString(R.string.copy) + ")";
			}else{
				newName = parent.getAbsolutePath() + "/" + nameWithoutExt + "(" + 
				sActivity.getResources().getString(R.string.copy)  + ")" + ext;				
			}
			
			File newFile = null;
			newFile = new File(newName);
			int nIndex = 1; 
			while (newFile.exists()) {
				if (null == ext || 0 == ext.length()) {
					newName = parent.getAbsolutePath() + "/" + nameWithoutExt + "("
							+ sActivity.getResources().getString(R.string.copy)
							+ nIndex + ")";
				} else {
					newName = parent.getAbsolutePath() + "/" + nameWithoutExt + "("
							+ sActivity.getResources().getString(R.string.copy)
							+ nIndex + ")" + ext;
				}

				newFile = new File(newName);
				nIndex += 1;
			}
			
			return newFile;
		}
		
		//copy files from one directory to another
		private boolean copy(File source, File dest) throws Exception {
			Log.i(TAG, "==> copy");
			
			// check the left space
			if (source.isFile()) {
				if (!hasEnoughSpaceForFile(source)) {
					onNoEnoughSpace();
				}
			}

			// avoid copy a folder to it's child folder
			// [check and improve]
			if (source.getName().equals(dest.getName())
					&& dest.getAbsolutePath().contains(source.getAbsolutePath())
					&& dest.getAbsolutePath().length() > source.getAbsolutePath().length()) {
				return false;
			}

			File newDes = new File(dest.getAbsolutePath() + "/" + source.getName());

			if (source.isDirectory()) {
				if (!newDes.exists()) {
					newDes.mkdir();
				} else {
					// [case]: des dir is existent
					if (newDes.equals(source)) {
						Log.i(TAG, "copy dir in its parent dir");

						newDes = getValidNewFolderName(dest, source.getName());
						if (null != newDes)
							newDes.mkdir();
					} else {
						if (!onSameDirExist(newDes.getName())) {
							Log.i(TAG, "get user feedback for same dir");
							return false;
						}
					}
				}

				File[] children = source.listFiles();
				for (int i = 0; i < children.length; i++) {
					if (isStop())
						break;
					copy(children[i], newDes);
				}
			} else {
				// source is a file
				if (isStop())
					return false;

				String newFileName = dest.getAbsolutePath() + "/" + source.getName();

				if (source.getAbsolutePath().equals(newFileName)) {
					Log.i(TAG, "copy file in its parent dir");
					newDes = getValidNewFileName(dest, source.getName());
				} else {
					// [case]: des file is existent
					newDes = new File(newFileName);
					if (newDes.exists() && !isStop()) {
						boolean isReplace = onSameFileExist(newDes.getName());
						if (isStop()) {
							Log.i(TAG, "stop thread after onSameFileExist");
							return false;
						} else {
							if (!isReplace) {
								return false;
							}
						}
					}

					if (sbNeedNotify == false)
						sbNeedNotify = needNotify(newDes);

					try {
						// Create channel on the source
						FileChannel srcChannel = new FileInputStream(source)
								.getChannel();
						// Create channel on the destination
						FileChannel dstChannel = new FileOutputStream(newDes)
								.getChannel();
						// Copy file contents from source to destination
						int nOffset = 0;

						while (nOffset < srcChannel.size() && !isStop()) {
							if (nOffset + COPY_BUFFER_MAXSIZE < srcChannel
									.size()) {
								dstChannel.transferFrom(srcChannel, nOffset,
										COPY_BUFFER_MAXSIZE);
								nOffset += COPY_BUFFER_MAXSIZE;
							} else {
								dstChannel.transferFrom(srcChannel, nOffset,
										srcChannel.size() - nOffset);
								break;
							}
						}
						// Close the channels
						srcChannel.close();
						dstChannel.close();

						if (isStop()) {
							newDes.delete();
							return false;
						}

						if (sbIsCut) {
							source.delete();
						}
						onUpdateProgress();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
			return true;
		}
		
		private boolean needNotify(File file) {
			Log.i(TAG, "==> needNotify");
			
			if (null == file || !file.exists()) {
				Log.e(TAG, "invalid parameter in needNotify");
				return false;
			} else {
				if (file.isDirectory())
					return false;

				String type = FileUtil.getMimeTypeOfFile(sActivity, file);
				if (null != type) {
					if (type.startsWith("image") || type.startsWith("audio")
							|| type.startsWith("video")) {
						return true;
					}
				}
				return false;
			}
		}
			
		protected void delete(File file) {
			Log.i(TAG, "==> delete ");

			if (file.isDirectory()) {
				File[] child = file.listFiles();
				// if the directory is empty, delete it directly.
				if (child.length > 0) {
					// if the directory is not empty,empty it before delete.
					int i;
					for (i = 0; i < child.length; i++) {
						if (isStop() || !file.exists())
							break;
						delete(child[i]);
						
						if(sbNeedNotify == false)
							sbNeedNotify = needNotify(child[i]);
					}
				}
			} else {
				if(sbNeedNotify == false)
					sbNeedNotify = needNotify(file);
			}
			if (!isStop() || file.exists())
				file.delete();
		}
		
		protected void realRun() {
			super.realRun();

			// get file count;
			getFileCount(slFiles);
			
			if (!isStop() && siFileCount > 1) {
				onShowProgDlg();
			}

				if (slFiles.size() == 1) {
					mbOnlyOneSrc = true;
				}

				for (int i = 0; i < slFiles.size() && !isStop(); i++) {
					File source = slFiles.get(i);

					// [case] source invalid
					if (!source.exists()) {
						Log.e(TAG, "source file is invalid : "
								+ source.getAbsolutePath());
						
						if (isSkipAll){
							boolean res = onSrcInvalid(source);
							sClipBoard.removeFile(source);
							if (res)
								continue;
							else{
								if (isStop())
									break;
							}
						}else{
							continue;
						}
					} else {
						Log.i(TAG, "the src is valid " + source.getAbsolutePath());
					}

					Log.i(TAG, "do copy now");
					
					if (isStop()){
						break;
					}

					try {
						boolean copied = copy(source, sfDest);
//						Log.i(TAG, "copied is " + String.valueOf(copied));
						if (copied && sbIsCut)
							if (source.exists())
								delete(source);
						// }
					} catch (Exception e) {
						e.printStackTrace();
						// [improve]
						onNoEnoughSpace();
						break;
					}
				}
				
				sendMsg(MSG_DISMISS_ALL_DIALOG);
				
				sendMsg(MSG_RES_PASTE_OK);

				Log.d(TAG, "copied files count : "
						+ sfDest.listFiles().length);

				if (sbNeedNotify) {
					Log.d(TAG, "to send file changed broadcast: "
							+ sfDest.getAbsolutePath());
					sActivity.sendBroadcast(new Intent(
							Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri
									.fromFile(sfDest)));
					if (sbIsCut) {
						// Log.d("wuqf",
						// "*******to send file changed broadcast: " +
						// mSrcFileList.get(0).getParentFile().getAbsolutePath());
						sActivity.sendBroadcast(new Intent(
								Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri
										.fromFile(mSrcFileList.get(0)
												.getParentFile())));
					}
					sbNeedNotify = false;
				}

				sbIsCut = false;
			}
	}	
	
	private static BaseActivity sActivity = null;
	private static ProgressDialog sProgDlg = null;
	private static ProgressDialog sWaitDlg = null;
	private static FilePasteThread sPasteThread = null;
	private static FileClipBoard sClipBoard = null;
	
	private static File sfDest = null;
	private static List<File> slFiles = null;
	private static int siFileCount = 0;
	private static boolean sbNeedNotify = false;
	private static int siFeedBack = INVALID_VALUE;
	
	
	private static boolean sbIsCut = false;
	
	private static void dismissAllDlg() {
		Log.i(TAG, "==> dismissAllDlg");
		if (null != sProgDlg) {
			sProgDlg.dismiss();
			sProgDlg = null;
		}
		if (null != sWaitDlg) {
			sWaitDlg.dismiss();
			sWaitDlg = null;
		}
	}
	
	private static void showWaitDialog(int rid) {
		Log.i(TAG, "==> showWaitDialog");
		if (null == sWaitDlg){
			sWaitDlg = new ProgressDialog(sActivity);

			sWaitDlg.setTitle(rid); //R.string.copy_progress
			sWaitDlg.setMessage(sActivity.getString(R.string.waiting));
			sWaitDlg.setCancelable(false);
			// mProgressDialog.setDefaultButton(false);
		}
		
		if (null != sWaitDlg)
			sWaitDlg.show();
	}
	
	private static void showProgDlg() {
		Log.i(TAG, "==> showProgDlg");
		if (null == sProgDlg){
			sProgDlg = new ProgressDialog(sActivity);
			sProgDlg.setTitle(R.string.delete_progress);
			sProgDlg.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
			sProgDlg.setCancelable(true);
			sProgDlg.setMax(siFileCount);
			sProgDlg.setButton(sActivity.getText(R.string.alert_dialog_cancel),
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog, int whichButton) {
							// User clicked No so do some stuff
							if (sPasteThread != null) {
								sPasteThread.stopIt();
								sActivity.sendEmptyMsg(MSG_RES_PASTE_CANCEL);
							}
							sPasteThread = null;
						}
					});
		}
		
		if (null != sProgDlg) {
			sProgDlg.show();
		}
	}
	
	private static void initData() {
		Log.i(TAG, "==> initData");
		sbNeedNotify = false;
		siFileCount = 0;
		siFeedBack = INVALID_VALUE;
		sbIsCut = false;
//		siCurProg = 0;
		
		if (null == slFiles) {
			slFiles = new ArrayList<File>();
		}
	}
	
	private static void initHandler() {
		Log.i(TAG, "==> initHandler");
		
		// for same file
		sActivity.addMsgHandler(MSG_PASTE_SAME_FILE, onSameFileHandler);
		
		// for only one same file
		sActivity.addMsgHandler(MSG_PASTE_SAME_FILE_ONLY_ONE, onSameFileOnlyOneHandler);
		
		// for same dir
		sActivity.addMsgHandler(MSG_PASTE_SAME_DIR, onSameDirHandler);
		
		// for only one same dir
		sActivity.addMsgHandler(MSG_PASTE_SAME_DIR_ONLY_ONE, onSameDirOnlyOneHandler);
		
		// for no space
		sActivity.addMsgHandler(MSG_PASTE_NOSPACE, onNoSpaceHandler);
		
		// for src invalid 
		sActivity.addMsgHandler(MSG_PASTE_SRC_INVALID, onSrcInvalidHandler);
		
		// show progress dialog
		FileHandler.MsgHandleFunc handler = new FileHandler.MsgHandleFunc() {
			// @Override
			public boolean onMessage(Message msg) {
				// TODO Auto-generated method stub
				if (null != sWaitDlg){
					sWaitDlg.dismiss();
					sWaitDlg = null;
				}
				showProgDlg();
				return false;
			}
		};
		sActivity.addMsgHandler(MSG_SHOW_PROGRESS_DIALOG, handler);
		
		// dismiss all dialogs
		handler = new FileHandler.MsgHandleFunc() {
			// @Override
			public boolean onMessage(Message msg) {
				// TODO Auto-generated method stub
				dismissAllDlg();
				return false;
			}
		};
		sActivity.addMsgHandler(MSG_DISMISS_ALL_DIALOG, handler);
	}
	
	private static void fillDataFromClip() {
		Log.i(TAG, "==> fillDataFromClip");
		if (null == slFiles) {
			slFiles = new ArrayList<File>();
		} else {
			slFiles.clear();
		}
		slFiles.addAll(sClipBoard.getItemsInList());
	}
	
	private static void init(){
		Log.i(TAG, "==> init");
		initData();
		initHandler();
	}
	
	private static void copyFileInClip() {
		Log.i(TAG, "==> copyFileInClip");
		init();
		fillDataFromClip();
		showWaitDialog(R.string.copy_progress);
		PasteFileInThread();
	}
	
	private static void cutFileInClip() {
		Log.i(TAG, "==> cutFileInClip");
		init();
		fillDataFromClip();
		sbIsCut = true;
		showWaitDialog(R.string.cut_progress);
		PasteFileInThread();
	}
	
	// [improve]
	private static boolean checkDestDir(File dest) {
		Log.i(TAG, "==> checkDestDir");
		if (!dest.isDirectory()) {
			Toast.makeText(sActivity, "Destination is not a folder",
					Toast.LENGTH_SHORT).show();
			return false;
		} else
			return true;
	}
	
	private static void onPasteOk() {
		sActivity.sendEmptyMsg(MSG_RES_PASTE_OK);
	}

	private static void onPasteCancel() {
		sActivity.sendEmptyMsg(MSG_RES_PASTE_CANCEL);
	}
	
	private static void onPasteFailed() {
		sActivity.sendEmptyMsg(MSG_RES_PASTE_FAILED);
	}
	
	private static boolean isInSameDir(File dest) {
		Log.i(TAG, "==> isInSameDir");
		if (sClipBoard.getFile(0).getParent()
				.equals(dest.getAbsolutePath())) {
			return true;
		}else
			return false;
	}
	
	private static void PasteFileInThread() {
		Log.i(TAG, "==> PasteFileInThread");
		sPasteThread = new FilePasteThread(sActivity);
		sPasteThread.start();
	}
	
	private static void checkClipThenCut() {
		Log.i(TAG, "==> checkClipThenCut");
		if (!checkDestDir(sfDest)){
			onPasteFailed();
			return;
		}else{
			if (isInSameDir(sfDest)){
				sClipBoard.clear();
				onPasteOk();
			}else{
				cutFileInClip();
			}
		}
	}
	
	private static void checkClipThenCopy() {
		Log.i(TAG, "==> checkClipThenCopy");
		if (!checkDestDir(sfDest)){
			onPasteFailed();
			return;
		}else{
			copyFileInClip();
		}		
	}
	
	// public interface
	public static void pasteFilesInClip(BaseActivity activity, File dest) {
		Log.i(TAG, "==> pasteFilesInClip");

		if (null == activity || null == dest || !dest.exists()) {
			Log.e(TAG, "invalid parameter in pasteFilesInClip");
			return;
		} else {
			sClipBoard = FileClipBoard.getInstance();
			if (sClipBoard.hasValidData()
					&& (FileClipBoard.OP_COPY == sClipBoard.getOperation()
							|| FileClipBoard.OP_CUT == sClipBoard.getOperation())) {
				sActivity = activity;
				sfDest = dest;
				
				if (FileClipBoard.OP_COPY == sClipBoard.getOperation()){
					checkClipThenCopy(); 
				}else{
					checkClipThenCut();
				}
			} else {
				Log.e(TAG, "no valid data in clip");
			}
		}
	}
}